package by.pavlyukevich.paymentsystem.services.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import by.pavlyukevich.paymentsystem.dataaccess.filter.CardLimitFilter;
import by.pavlyukevich.paymentsystem.dataaccess.filter.TransactionFilter;
import by.pavlyukevich.paymentsystem.dataaccess.impl.TransactionDaoImpl;
import by.pavlyukevich.paymentsystem.datamodel.Account;
import by.pavlyukevich.paymentsystem.datamodel.Transaction;
import by.pavlyukevich.paymentsystem.datamodel.card.Card;
import by.pavlyukevich.paymentsystem.datamodel.card.CardLimit;
import by.pavlyukevich.paymentsystem.datamodel.card.LimitType;
import by.pavlyukevich.paymentsystem.services.AccountService;
import by.pavlyukevich.paymentsystem.services.TransactionService;
import by.pavlyukevich.paymentsystem.services.card.CardLimitService;
import by.pavlyukevich.paymentsystem.services.card.CardService;

@Service
public class TransactionServiceImpl extends
		AbstractServiceImpl<Integer, Transaction, TransactionDaoImpl> implements TransactionService {

	@Inject
	private CardService cardService;

	@Inject
	private AccountService accountService;

	@Inject
	private CardLimitService cardLimitService;

	@Override
	@SuppressWarnings("incomplete-switch")
	public String validateAndBlock(Transaction transaction) {

		Card senderCard = transaction.getSenderCard();
		if (cardService.get(senderCard.getId()).getIsBlocked()) {
			return "t.error.cardIsBlocked";
		}
		if (accountService.get(senderCard.getAccount().getId()).getIsBlocked()) {
			return "t.error.accountIsBlocked";
		}
		if (accountService.get(senderCard.getAccount().getId()).getBalance() < transaction
				.getAmount()) {
			return "t.error.insufficientFunds";
		}

		Calendar calendar = new GregorianCalendar();
		List<CardLimit> cardLimitList = cardLimitService.getList(new CardLimitFilter().setCard(
				transaction.getSenderCard()).setDate(transaction.getDate()));

		for (CardLimit cardLimit : cardLimitList) {
			if (cardLimit.getLimitType() == LimitType.SINGLE) {
				if (cardLimit.getAmount() < transaction.getAmount()) {
					return "t.error.exceededLimit";
				}
			} else {
				calendar.setTime(transaction.getDate());
				int fieldNumber = 0;
				switch (cardLimit.getLimitType()) {
				case WEEKLY:
				case DAYLI:
					fieldNumber = Calendar.DAY_OF_MONTH;
					break;
				case MONTHLY:
					fieldNumber = Calendar.MONTH;
					break;
				}
				calendar.add(fieldNumber, (cardLimit.getLimitType() == LimitType.WEEKLY) ? -7 : -1);

				Date periodBegin = calendar.getTime();

				TransactionFilter transactionFilter = new TransactionFilter()
						.setBeginPeriod(periodBegin).setEndPeriod(transaction.getDate())
						.setSenderCard(transaction.getSenderCard());

				Long amount = cardLimit.getAmount();
				Integer quantity = cardLimit.getQuantity();
				Double sum = getSum(transactionFilter);
				sum = (sum == null) ? 0 : sum;
				long count = getCount(transactionFilter);
				if ((amount != null && amount > 0 && amount < sum + transaction.getAmount())
						|| (quantity != null && quantity > 0 && quantity < count + 1)) {
					if (cardLimit.getBlock()) {
						senderCard.setIsBlocked(true);
						cardService.saveOrUpdate(senderCard);
						return "t.error.cardBlocked";
					}
					return "t.error.exceededLimit";

				}
			}
		}
		return null;
	}

	@Override
	public Double getSum(TransactionFilter transactionFilter) {
		return dao.getSum(transactionFilter);
	}

	@Override
	public void executeTransaction(Transaction transaction) {
		Account payeeAccount = transaction.getPayeeAccount();
		payeeAccount.setBalance(payeeAccount.getBalance() + transaction.getAmount());
		accountService.saveOrUpdate(payeeAccount);
		Account senderAccount = transaction.getSenderAccount();
		senderAccount.setBalance(senderAccount.getBalance() - transaction.getAmount());
		accountService.saveOrUpdate(senderAccount);
	}
}
